package com.gitee.beiding.template_execel;

import org.apache.poi.xssf.usermodel.XSSFRow;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.*;

public final class Renderer {


    private Renderer() {
    }


    /**
     *
     * @param data          被渲染的数据
     * @param templateSheet 模板sheet页
     * @param targetSheet   接收渲染结果的sheet页
     */

    public static void render(Data data, XSSFSheet templateSheet, XSSFSheet targetSheet) {
        render(data.getData(), templateSheet, targetSheet);
    }

    /**
     * 使用Map作为数据源进行渲染
     *
     * @param data          被渲染的数据
     * @param templateSheet 模板sheet页
     * @param targetSheet   接收渲染结果的sheet页
     */
    public static void render(Map<String, Object> data, XSSFSheet templateSheet, XSSFSheet targetSheet) {

        //放入目标数据
        Js.putAll(data);

        //复制样式
        PoiUtils.copySheet(templateSheet, targetSheet);

        //处理列宽
        PoiUtils.handleColumnWidth(templateSheet, targetSheet);

        //读取内容
        Map<Integer, Map<Integer, Object>> templateRow = PoiUtils.readSheet(templateSheet);

        //模板展开为实际数据
        Map<Integer, List<Map<Integer, Object>>> map = templateRowToValueRows(templateRow);

        //复制数据
        //PoiUtils.copySheetDataWithData(srcSheet, targetSheet, map);

        //TODO 赋值数据
        TemplateUtils.copySheetDataWithData(templateSheet, targetSheet, map);

        Js.recycle();

    }

    /**
     * 使用Data对象渲染生成一个Sheet页
     *
     * @param data     数据源
     * @param template 模板sheet页
     * @return 渲染的结果
     * @throws Exception 所有可能出现的异常
     */
    public static XSSFWorkbook render(Data data, InputStream template) throws Exception {
        return render(data.getData(), template);
    }

    /**
     * 使用Map对象渲染生成一个Sheet页
     *
     * @param data     数据源
     * @param template 模板sheet页
     * @param templateSheetNames 需要被渲染的sheet页名称
     * @return 渲染的结果
     * @throws Exception 所有可能出现的异常
     */
    public static XSSFWorkbook render(Map<String, Object> data, InputStream template, String... templateSheetNames) throws Exception {

        Js.putAll(data);
        ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
        byte[] bytes = new byte[1024];
        int len;
        while ((len = template.read(bytes)) != -1) {
            byteArrayOutputStream.write(bytes, 0, len);
        }

        XSSFWorkbook src = PoiUtils.read(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));

        XSSFWorkbook target = PoiUtils.read(new ByteArrayInputStream(byteArrayOutputStream.toByteArray()));

        List<String> list = Arrays.asList(templateSheetNames);



        //遍历所有的sheet页
        for (int i = 0; i < src.getNumberOfSheets(); i++) {
            XSSFSheet srcSheet = src.getSheetAt(i);

            XSSFSheet targetSheet = target.getSheet(srcSheet.getSheetName());

            if (list.size() > 0 && !list.contains(srcSheet.getSheetName())) {
                continue;
            }

            if (targetSheet == null) {
                target.createSheet(srcSheet.getSheetName());
            } else {
                for (int j = 0; j <= targetSheet.getLastRowNum(); j++) {
                    XSSFRow row = targetSheet.getRow(j);
                    if (row != null) {
                        targetSheet.removeRow(row);
                    }
                }
            }

            //复制样式
            PoiUtils.copySheet(srcSheet, targetSheet);

            //处理列宽
            PoiUtils.handleColumnWidth(srcSheet, targetSheet);

            //读取内容
            Map<Integer, Map<Integer, Object>> templateRow = PoiUtils.readSheet(srcSheet);


            //模板展开为实际数据
            Map<Integer, List<Map<Integer, Object>>> map = templateRowToValueRows(templateRow);

            //复制数据
            //PoiUtils.copySheetDataWithData(srcSheet, targetSheet, map);

            //TODO 复制数据
            TemplateUtils.copySheetDataWithData(srcSheet, targetSheet, map);

        }


        Js.recycle();

        return target;

    }

    private static Map<Integer, List<Map<Integer, Object>>> templateRowToValueRows(Map<Integer, Map<Integer, Object>> templateRow) {

        Map<Integer, List<Map<Integer, Object>>> map = new HashMap<>();

        templateRow.forEach((rowNumber, row) -> {

            Map<String, Integer> max = new HashMap<>();

            Map<Integer, TemplateCell> values = new HashMap<>();

            //遍历一行模板
            row.forEach((colNumber, col) -> {

                //解析结果
                TemplateCell parse = TemplateCell.parse(col);
                try {
                    parse.exe();
                } catch (Exception e) {
                    throw new RuntimeException(e);
                }

                values.put(colNumber, parse);

                Map<String, Integer> indexMax = parse.getIndexMax();
                if (indexMax != null) {
                    indexMax.forEach((name, maxValue) -> {
                        Integer integer = max.get(name);
                        if (integer == null || maxValue > integer) {
                            max.put(name, maxValue);
                        }
                    });
                }

            });

            //
            if (max.size() > 0) {
                AutoIncrementNumber autoIncrementNumber = new AutoIncrementNumber();
                autoIncrementNumber.init(max);

                List<Map<Integer, Object>> list = new ArrayList<>();
                //自增
                out:
                while (autoIncrementNumber.autoIncrement()) {

                    int index = autoIncrementNumber.getIndex();

                    //为什么无效???

                    Js.set("_index", index);

                    Map<Integer, Object> line = new HashMap<>();

                    //如果有一个元素是无效的就直接返回
                    for (Integer colValue : values.keySet()) {
                        TemplateCell tc = values.get(colValue);
                        try {
                            line.put(colValue, tc.get(autoIncrementNumber.getValues()));
                        } catch (InvalidIndexException e) {
                            continue out;
                        }
                    }
                    if (line.size() > 0) {
                        list.add(line);
                    }
                }
                Js.remove("_index");

                if (list.size() > 0) {
                    map.put(rowNumber, list);
                }

            } else {
                List<Map<Integer, Object>> list = new ArrayList<>();
                Map<Integer, Object> line = new HashMap<>();
                values.forEach((colValue, tc) -> {
                    line.put(colValue, tc.getSingleResult());
                });
                if (line.size() > 0) {
                    list.add(line);
                    map.put(rowNumber, list);
                }
            }

            try {

                //清空缓存,防止内存溢出
                Js.call("_clearCache");
            } catch (Exception e) {

            }

        });

        return map;
    }

    /**
     * 使用Map作为源数据,将数据渲染到目标文件中
     *
     * @param data               源数据
     * @param template           模板Excel文件
     * @param target             目标Excel文件
     * @param templateSheetNames 模板文件中需要被渲染的Sheet页名称,如果未指定则渲染全部
     * @throws Exception 所有可能的异常
     */
    public static void render(Map<String, Object> data, InputStream template, OutputStream target, String... templateSheetNames) throws Exception {
        XSSFWorkbook render = render(data, template, templateSheetNames);

        PoiUtils.write(render, target);
    }

    /**
     * 使用Data作为源数据,将数据渲染到目标文件中
     *
     * @param data               源数据
     * @param template           模板Excel文件
     * @param target             目标Excel文件
     * @param templateSheetNames 模板文件中需要被渲染的Sheet页名称,如果未指定则渲染全部
     * @throws Exception 所有可能的异常
     */
    public static void render(Data data, InputStream template, OutputStream target, String... templateSheetNames) throws Exception {
        render(data.getData(), template, target, templateSheetNames);
    }






}

